﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpiritEngine.Source.Utilities
{
    public class ParamVerifier
    {
        #region Globals

        public struct ParamInfo
        {
            public bool AllowNull;
            public Type ParamType;
        }

        #endregion

        #region Constants/Enums

        #endregion


        #region Structs

        #endregion


        #region Static Member Variables and Methods

        #endregion


        #region Member Variables

        List<ParamInfo> m_ParamList;

        #endregion


        #region Events/Delegates

        #endregion


        #region Constructors and Destructors (aka Finalizers)

        public ParamVerifier()
        {
            m_ParamList = new List<ParamInfo>();
        }

        #endregion


        #region Non-Public Methods

        // This section is for methods that are declared as private, protected, protected internal, or internal.

        #endregion


        #region Public Methods

        /// <summary>
        /// Adds a new parameter to the parameter list.
        /// </summary>
        /// <param name="type">The expected type of the parameter.</param>
        /// <param name="allowNull">Whether the value null is allowed to be passed into this parameter.</param>
        public void AddParam(Type type, bool allowNull)
        {
            m_ParamList.Add(new ParamInfo() { AllowNull = allowNull, 
                                              ParamType = type });
        }


        /*
        /// <summary>
        /// This function verifies a parameter list to a function that takes a variable parameter list.  This parameter list is in
        /// the form of a ParamInfo array.  This function is for use with functions such as the Initialize() methods of many objects.
        ///
        /// NOTE: This function is enabled when the active build config defines the symbol VERIFY_PARAMS.  This is so it can be disabled/enabled indepenent of whether it is compiled in debug mode or not.  The calling function has calls to this function in a #if directive that checks for the VERIFY_PARAMS symbol.
        /// See the EngineBase.Initialize() method and other object's Initialize() methods for more information.
        /// 
        /// IMPORTANT:
        /// This function can be used to check functions with multiple valid parameter lists as well.  You just do one call of this function for each
        /// valid parameter set that the function can except.  Most functions will not have more than 1 or 2 possible parameter sets though.
        /// </summary>
        /// <param name="m_ParamList">An array containing all of the parameter info.</param>
        /// <param name="allowNoParams">Indicates whether an empty parameter list is allowed or not.</param>         
        /// <returns>True if the parameters are valid, or false otherwise.</returns>
        public bool VerifyParameters(bool allowNoParams)
        {
            bool result = true;


            // If there are 0 parameters and this is not allowed, then set result to false.
            if ((!allowNoParams) && (m_ParamList.Count < 1))
            {
                result = false;
            }
            else
            {

                // Check that each parameter is the correct type.
                for (int i = 0; i < m_ParamList.Count; i++)
                {
                    if ((m_ParamList[i].Value == null) &&
                        (!m_ParamList[i].AllowNull))
                    {
                        return false;
                    }

                    if (m_ParamList[i].ParamType == null)
                        throw new ArgumentException("SE_Debug.VerifyParameters(ParamInfo[], bool): The expected type for a parameter cannot be null!");

                    if (!(m_ParamList[i].ParamType.IsAssignableFrom(m_ParamList[i].Value.GetType())))
                    {
                        result = false;
                        break;
                    }
                }

            }


            return result;
        }


        /// <summary>
        /// This overload of the VerifyParameters() method is for use when a parameter list can vary in length.  This version of the function takes a single Type variable.
        /// When the Type parameter is not null, all parameters must be of the specified type.  When it is null, the function
        /// will verify each parameter is of the expected type specified for it in the m_ParamList array.
        ///
        /// NOTE: This function is enabled when the active build config defines the symbol VERIFY_PARAMS.  This is so it can be disabled/enabled indepenent of whether it is compiled in debug mode or not.  The calling function has calls to this function in a #if directive that checks for the VERIFY_PARAMS symbol. 
        /// See the EngineBase.Initialize() method and other object's Initialize() methods for more information.
        /// 
        /// IMPORTANT:
        /// This function can be used to check functions with multiple valid parameter lists as well.  You just do one call of this function for each
        /// valid parameter set that the function can except.  Most functions will not have more than 1 or 2 possible parameter sets though.         
        /// </summary>
        /// <param name="m_ParamList">The parameter list info.</param>
        /// <param name="minParams">Indicates the smallest number of parameters the function can accept.</param>
        /// <param name="maxParams">Indicates the largest number of parameters the function can accept. Set this to a negative to indicate that the function doesn't have an upper limit on params.</param>
        /// <param name="type">The type that all of the parameters are expected to be.  For differing types, set this to null to use the expect types passed in via the m_ParamList parameter.</param>
        /// <returns>True if the parameters are valid, or false otherwise.</returns>
        public static bool VerifyParameters_VariableLength(ParamInfo[] m_ParamList, int minParams, int maxParams, Type type)
        {
            bool result = true;

            // Check if minParams is negative.
            if (minParams < 0)
                throw new ArgumentException("SE_Debug.VerifyParameters(ParamInfo[], int, int, Type): minParams cannot be negative!");

            // Check that minParams is less than maxParams
            if (maxParams >= 0)
            {
                if (minParams > maxParams)
                    throw new ArgumentException("SE_Debug.VerifyParameters(ParamInfo[], int, int, Type): minParams must be less than or equal to maxParams, except when giving maxParams a negative value!");
            }


            // Check that the number of parameters is not too low.
            if (m_ParamList.Length < minParams)
            {
                result = false;
            }
            // Check that the number of parameters is not too high.
            else if ((maxParams > 0) &&
                      (m_ParamList.Length > maxParams))
            {
                result = false;
            }
            else
            {
                Type t = null;
                // Check that each parameter is the correct type.
                for (int i = 0; i < m_ParamList.Length; i++)
                {
                    // If the type parameter is not null, then all parameters must be of that type.
                    if (type != null)
                    {
                        if (!(type.IsAssignableFrom(m_ParamList[i].Value.GetType())))
                        {
                            result = false;
                            break;
                        }
                    }
                    // Since the Type parameter is null, use the expected type parameter passed in for each parameter
                    // to validate the type of each param.  This allows the use of differing param types if you want.
                    else
                    {
                        if (m_ParamList[i].ParamType == null)
                            throw new ArgumentException("SE_Debug.VerifyParameters(ParamInfo[], int, int, Type): The expected type for a parameter cannot be null!");


                        if (!(m_ParamList[i].ParamType.IsAssignableFrom(m_ParamList[i].Value.GetType())))
                        {
                            result = false;
                            break;
                        }

                    }
                }
            }


            return result;
        }


        /// <summary>
        /// This overload of the VerifyParameters() method is for use when the calling function expects no parameters.
        ///
        /// NOTE: This function is enabled when the active build config defines the symbol VERIFY_PARAMS.  This is so it can be disabled/enabled indepenent of whether it is compiled in debug mode or not.  The calling function has calls to this function in a #if directive that checks for the VERIFY_PARAMS symbol. 
        /// See the EngineBase.Initialize() method and other object's Initialize() methods for more information.        
        /// </summary>
        /// <param name="m_ParamList">The parameter list info.</param>
        /// <returns>True if the parameters are valid, or false otherwise.</returns>
        public static bool VerifyParameters_NoParameters(ParamInfo[] m_ParamList)
        {
            if (m_ParamList.Length != 0)
                return false;


            return true;
        }

        */

        #endregion


        #region Interface Methods

        // This section is for methods that are part of the interfaces that the class implements.

        #endregion


        #region Event Invocation Methods

        // This section is for methods that invoke the events of this class.  For example:
        //
        // Invoke the Changed event; called whenever list changes
        // protected virtual void OnChanged(EventArgs e) 
        // {
        //		if (Changed != null)
        //			Changed(this, e);
        // }

        #endregion


        #region Operators

        #endregion


        #region Debug Methods

        // This will compile the debug code only if the DEBUG symbol is defined.
        // To define this symbol for all source files in the project, open the Project Properties,
        // and go to the Build tab.  Check the "Define DEBUG constant" checkbox.  With this box checked,
        // the compiler will automatically define the symbol DEBUG.
#if (DEBUG)
        // put all debug methods in here...
#endif

        #endregion


        #region Properties

        /// <summary>
        /// Returns the number of parameters that have been added.
        /// </summary>
        public int Count
        {
            get
            {
                return m_ParamList.Count;
            }
        }

        #endregion

    }
}
